package com.i72.freeway;

import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.SystemPropertyUtils;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author jiangj
 * @version 1.0.0
 * @ClassName SpringResourceHelper.java
 * @Description TODO
 * @createTime 2021年12月28日 14:22:00
 */
public class SpringResourceHelper {

    private static final ResourcePatternResolver resolver              = new PathMatchingResourcePatternResolver();
    private static final MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver);

    public static ResourcePatternResolver getResolver() {
        return resolver;
    }

    /**
     * !!使用完请关闭InputStream!!
     */
    public static List<InputStream> getFiles(String locationPattern) throws IOException {
        List<InputStream> list = new ArrayList<>();
        Resource[] resources = resolver.getResources(locationPattern);
        for (Resource resource : resources) {
            list.add(resource.getInputStream());
        }
        return list;
    }

    public static Resource[] getResources(String locationPattern) throws IOException {
        return resolver.getResources(locationPattern);
    }

    /**
     * !!使用完请关闭InputStream!!
     */
    public static InputStream getFile(String location) throws IOException {
        return resolver.getResource(location).getInputStream();
    }

    public static List<Class> getClasses(String location) {
        return getClasses(location, null, null);
    }

    public static List<Class> getClasses(String location, TypeFilter[] includeFilters, TypeFilter[] excludeFilters) {
        return getClasses(location, includeFilters, excludeFilters, true);
    }

    public static List<Class> scanPackage(Class annotation, String... packages){
        List<Class> classList = new ArrayList<>();

        SimpleMetadataReaderFactory readerFactory = new SimpleMetadataReaderFactory();

        PathMatchingResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();

        Resource[] resources = null;
        List<Resource> resourceList = new ArrayList<>();
        for(String p:packages) {
            String path = p.replace(".","/");
            String resourcePath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX+path;

            if(!resourcePath.endsWith("/")){
                resourcePath+="/";
            }

            resourcePath +="**/*.class";
            try {

                resources = resourcePatternResolver.getResources(resourcePath);
                if(resources!=null && resources.length>0) {
                    resourceList.addAll(Arrays.asList(resources));
                }
            }catch (Exception e){

            }
        }

        String className = "";
        if(resourceList!=null && resourceList.size()>0) {
            for (Resource r : resourceList){
                try {
                    MetadataReader metadataReader = readerFactory.getMetadataReader(r);

                    AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
                    ClassMetadata classMetadata = metadataReader.getClassMetadata();

                    if(annotation!=null){

                        if(annotationMetadata.getAnnotationTypes().contains(annotation.getName())){
                            className = classMetadata.getClassName();
                        }else{
                            className = "";
                        }
                    }else{
                        className = classMetadata.getClassName();
                    }

                    if(!StringUtils.isEmpty(className)) {
                        classList.add(Class.forName(className));
                    }

                }catch (Exception e){

                }
            }
        }

        return classList;
    }

    public static List<Class> getClasses(String location, TypeFilter[] includeFilters, TypeFilter[] excludeFilters, boolean isPathFill) {
        List<Class> classes = new ArrayList<>();
        try {
            String packageSearchPath;
            if (isPathFill) {
                packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
                        + ClassUtils.convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(location)) + "/**/*.class";
            } else {
                packageSearchPath = location;
            }
            Resource[] resources = resolver.getResources(packageSearchPath);

            for (Resource resource : resources) {
                if (resource.isReadable()) {
                    MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
                    if (matches(metadataReader, includeFilters) && !matches(metadataReader, excludeFilters)) {
                        classes.add(Class.forName(metadataReader.getClassMetadata().getClassName()));
                    }
                }
            }
        } catch (Exception e) {
            throw new BeanDefinitionStoreException("failure during classpath scanning", e);
        }
        return classes;
    }

    private static boolean matches(MetadataReader metadataReader, TypeFilter[] filters) throws IOException {
        if (filters!=null && filters.length>0) {
            for (TypeFilter tf : filters) {
                if (tf.match(metadataReader, metadataReaderFactory)) {
                    return true;
                }
            }
        }
        return false;
    }

}
